package cn.zhx.xa.bi.service.impl;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.zhx.xa.bi.dao.BiChartDao;
import cn.zhx.xa.model.enums.ChartStatus;
import cn.zhx.xa.bi.manage.AiManage;
import cn.zhx.xa.bi.mapper.BiChartMapper;
import cn.zhx.xa.bi.mq.BiMessageProducer;
import cn.zhx.xa.bi.utils.ExcelUtils;

import cn.zhx.xa.model.dto.bi.ChartQueryRequest;
import cn.zhx.xa.model.dto.bi.GenChartByAiRequest;
import cn.zhx.xa.model.entity.BiChart;
import cn.zhx.xa.model.vo.bi.BiVo;
import cn.zhx.xa.model.vo.common.ResultCodeEnum;
import cn.zhx.xa.service.exception.BusinessException;
import cn.zhx.xa.service.manager.RedisLimiterManager;
import cn.zhx.xa.utils.AuthContextUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RateIntervalUnit;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import cn.zhx.xa.bi.service.BiChartService;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author DELL
 * @description 针对表【bi_chart(图表信息表)】的数据库操作Service实现
 * @createDate 2024-02-04 19:53:56
 */
@Service
@Slf4j
public class BiChartServiceImpl implements BiChartService {

    /**
     * 文件大小 1M
     */
    private static final long FILE_MAX_SIZE = 1 * 1024 * 1024L;
    /**
     * 文件后缀白名单
     */
    private static final List<String> VALID_FILE_SUFFIX = Arrays.asList("xlsx", "csv", "xls", "json");

    @Resource
    private AiManage aiManage;

    @Resource
    @Lazy
    private BiChartMapper biChartMapper;

    @Resource
    private BiChartDao biChartDao;

    @Resource
    private RedisLimiterManager redisLimiterManager;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private BiMessageProducer biMessageProducer;

//    @Override
//    public BiVo genChartByAi(MultipartFile multipartFile, GenChartByAiRequest genChartByAiRequest) {
//        String chartName = genChartByAiRequest.getChartName();
//        String chartType = genChartByAiRequest.getChartType();
//        String goal = genChartByAiRequest.getGoal();
//        Long uid = AuthContextUtil.get();
//        //压缩后的数据(将MultiPartFile转换为csv格式)
//        String csvData = ExcelUtils.excelToCsv(multipartFile);
//
//        //1.校验
//        //校验文件
//        checkFile(multipartFile);
//        //校验参数
//        checkParams(genChartByAiRequest);
//
//        //2.限流
//        redisLimiterManager.doRateLimit("genChartByAi_" + uid,2,1, RateIntervalUnit.MINUTES);
//
//        //3.构建用户输入信息
//        BiChart biChart = new BiChart();
//        biChart.setChartType(chartType);
//        biChart.setGoal(goal);
//        biChart.setChartData(csvData);
//        StringBuilder userInput = constructUserInput(biChart);
//
//        //4.发送请求
//        String result = aiManage.doChat(String.valueOf(userInput));
//
//        //5.整理结果
//        BiVo biVo = getOrganizeAiData(result);
//
//        //6.插入到数据库
//        BiChart chart = new BiChart();
//        chart.setChartName(chartName);
//        chart.setGoal(goal);
//        chart.setChartData(csvData);
//        chart.setChartType(chartType);
//        chart.setGenChart(biVo.getGenChart());
//        chart.setGenResult(biVo.getGenResult());
//        chart.setUserId(uid);
//        boolean save = biChartDao.save(chart);
//        if (!save) throw new BusinessException(ResultCodeEnum.SAVE_ERROR);
//
//        //构造响应结果
//        biVo.setChartId(chart.getId());
//        return biVo;
//    }

    @Override
    public Page<BiChart> page(ChartQueryRequest chartQueryRequest, Integer pageNum, Integer pageSize) {
        if (!ObjectUtil.isAllNotEmpty(pageNum, pageSize)) {
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        }
        Long uid = AuthContextUtil.get();
        if (uid == null) throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);
        return biChartDao.findByPage(chartQueryRequest, new Page<BiChart>(pageNum, pageSize), uid);
    }

    @Override
    public BiVo genChartByAiAsync(MultipartFile multipartFile, GenChartByAiRequest genChartByAiRequest){
        String chartName = genChartByAiRequest.getChartName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();
        Long uid = AuthContextUtil.get();
        String csvData = ExcelUtils.excelToCsv(multipartFile);

        //1.校验
        //校验文件
        checkFile(multipartFile);
        //校验参数
        checkParams(genChartByAiRequest);

        //2.限流
        redisLimiterManager.doRateLimit("genChartByAi_" + uid,2,1, RateIntervalUnit.MINUTES);

        //3.插入到数据库
        BiChart chart = new BiChart();
        chart.setChartName(chartName);
        chart.setGoal(goal);
        chart.setChartData(csvData);
        chart.setChartType(chartType);
        chart.setUserId(uid);
        chart.setChartStatus(ChartStatus.WAIT.getValue());
        boolean save = biChartDao.save(chart);
        if(!save) throw new BusinessException(ResultCodeEnum.SAVE_ERROR);

        //4.构建用户输入信息
        String userInput = constructUserInput(chart);
        log.info("新增图表id为" + chart.getId());

        // todo 建议处理任务队列满了后，抛异常的情况
        CompletableFuture.runAsync(() -> {
            // 先修改图表任务状态为 “执行中”。等执行成功后，修改为 “已完成”、保存执行结果；
            // 执行失败后，状态修改为 “失败”，记录任务失败信息。
            BiChart updateChart = new BiChart();
            log.info("新增图表id为：" + chart.getId());
            updateChart.setId(chart.getId());
            updateChart.setChartStatus(ChartStatus.RUNNING.getValue());
            boolean b = biChartDao.updateById(updateChart);
            if (!b) {
                handleChartUpdateError(chart.getId(), "更新图表状态失败");
                return;
            }

            // 发送请求
            String chartResult = aiManage.doChat(userInput);
            // 处理ai生成的数据
            BiVo biVo = getOrganizeAiData(chart.getId(),chartResult);
            String genChart = biVo.getGenChart();
            String genResult = biVo.getGenResult();
            biVo.setChartId(chart.getId());

            // 更新图表信息
            BiChart updateChartResult = new BiChart();
            updateChartResult.setId(chart.getId());
            updateChartResult.setGenChart(genChart);
            updateChartResult.setGenResult(genResult);
            updateChartResult.setChartStatus(ChartStatus.SUCCEED.getValue());
            int updateResult = biChartMapper.updateById(updateChartResult);
            if (updateResult <= 0) {
                handleChartUpdateError(chart.getId(), "更新图表成功信息失败");
            }
        }, threadPoolExecutor);

        BiVo biVo = new BiVo();
        biVo.setChartId(chart.getId());
        return biVo;
    }

    @Override
    public BiVo getBiResponseAsyncMq(MultipartFile multipartFile,GenChartByAiRequest genChartByAiRequest) {
        String chartName = genChartByAiRequest.getChartName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();
        Long uid = AuthContextUtil.get();
        String csvData = ExcelUtils.excelToCsv(multipartFile);

        //1.校验
        //校验文件
        checkFile(multipartFile);
        //校验参数
        checkParams(genChartByAiRequest);

        //2.限流
        redisLimiterManager.doRateLimit("genChartByAi_" + uid,2,1, RateIntervalUnit.MINUTES);

        //3.插入到数据库
        BiChart chart = new BiChart();
        chart.setChartName(chartName);
        chart.setGoal(goal);
        chart.setChartData(csvData);
        chart.setChartType(chartType);
        chart.setUserId(uid);
        chart.setChartStatus(ChartStatus.WAIT.getValue());
        boolean save = biChartDao.save(chart);
        if(!save) throw new BusinessException(ResultCodeEnum.SAVE_ERROR);

        Long chartId = chart.getId();

        //4.发送消息到MQ
        biMessageProducer.sendMessage(String.valueOf(chartId));

        BiVo biVo = new BiVo();
        biVo.setChartId(chart.getId());
        return biVo;
    }

    //更新图表信息失败
    private void handleChartUpdateError(Long id,String execMessage) {
        log.error(execMessage);
        BiChart updateChart = new BiChart();
        updateChart.setId(id);
        updateChart.setExecMessage(execMessage);
        updateChart.setChartStatus(ChartStatus.FAILED.getValue());
        int i = biChartMapper.updateById(updateChart);
        if(i <= 0){
            log.error(execMessage+"更新失败");
        }
    }


    @Override
    public BiVo getOrganizeAiData(Long id,String chartResult) {
        String[] splits = chartResult.split("=====");
        if (splits.length < 3) {
            handleChartUpdateError(id, "AI返回数据格式错误");
        }

        String genChart = splits[1].trim();
        String genResult = splits[2].trim();

        BiVo biVo = new BiVo();
        biVo.setGenChart(genChart);
        biVo.setGenResult(genResult);
        return biVo;
    }


    @Override
    public Void deleteChartById(Long chartId) {
        if(ObjectUtil.isEmpty(chartId)) throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        boolean b = biChartDao.removeById(chartId);
        if(!b) throw new BusinessException(ResultCodeEnum.DELETE_ERROR);
        return null;
    }

    /**
     * 构造用户输入信息
     *
     * @param chart
     * @return
     */
    @NotNull
    @Override
    public String constructUserInput(BiChart chart) {
        String goal = chart.getGoal();
        String chartType = chart.getChartType();
        String csvData = chart.getChartData();

        StringBuilder userInput = new StringBuilder();
        userInput.append("分析需求:").append("\n");
        // 拼接分析目标
        String userGoal = goal;
        if (StrUtil.isNotBlank(chartType)) {
            userGoal += "，请使用" + chartType;
        }
        userInput.append(userGoal).append("\n");
        userInput.append("原始数据:").append("\n");
        userInput.append(csvData).append("\n");
        return userInput.toString();
    }


    /**
     * 校验文件
     * @param multipartFile
     */
    private static void checkFile(MultipartFile multipartFile) {
        // 校验文件
        long fileSize = multipartFile.getSize();
        String originalFilename = multipartFile.getOriginalFilename();
        // 校验文件大小
        if(fileSize > FILE_MAX_SIZE) throw new BusinessException(ResultCodeEnum.FILESIZE_ERROR);
        // 校验文件后缀
        String suffix = FileUtil.getSuffix(originalFilename);
        if(!VALID_FILE_SUFFIX.contains(suffix)) throw new BusinessException(ResultCodeEnum.FILE_SUFFIX_ERROR);
    }

    /**
     * 校验参数
     * @param genChartByAiRequest
     */
    private static void checkParams(GenChartByAiRequest genChartByAiRequest) {
        String chartName = genChartByAiRequest.getChartName();
        String chartType = genChartByAiRequest.getChartType();
        String goal = genChartByAiRequest.getGoal();
        if (StrUtil.isEmpty(goal)) {
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        }
        if (StrUtil.isNotEmpty(chartName) && chartName.length() > 100) {
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        }
    }

}




